﻿using Microsoft.AspNetCore.SignalR;
using MultiAgentsClient.ChatDataStructures;
using MultiAgentsServer.Scripts.Database;
using MultiAgentsServer.Scripts.Hubs;
using MultiAgentsServer.Scripts.Services.DataModels;
using MultiAgentsServer.Scripts.Shared;
using MultiAgentsShared;
using static MultiAgentsServer.Scripts.Database.DatabaseRepository;
using static MultiAgentsServer.Scripts.Shared.ModelConsts;

namespace MultiAgentsServer.Scripts.Services;

public class AgentService
{
    #region Commands: AgentSignalRHub -> AgentConfigService
    public record CreateNewAgentCommand(string ConnectionId, string SessionId, int UserId);
    public record GenerateMessageCommand(string ConnectionId, string SessionId, int UserId, List<ChatMessageData> Messages, float Temperature, string Model);
    public record LoadAgentByJsonRequestCommand(string ConnectionId, string SessionId, int UserId, string AgentJson);
    public record RemoveAgentCommand(string ConnectionId, string SessionId, int UserId, int AgentId);
    public record UpdateAgentCommand(string ConnectionId, string SessionId, int UserId, AgentTemplate AgentTemplate);
    #endregion

    private readonly IEventBus eventBus;
    private readonly DataBaseOperationService dataBaseOperationService;
    private readonly DatabaseRepository databaseRepository;
    private readonly IHubContext<SignalRAgentHub> AgentContext;
    private readonly UserCookieDataModel userCookieDataModel;
    private readonly LlmComEntryPoint llmComEntryPoint;

    public AgentService(
        IEventBus eventBus, 
        DatabaseRepository databaseRepository,
        LlmComEntryPoint llmComEntryPoint,
        IHubContext<SignalRAgentHub> signalRAgentHubContext,
        DataBaseOperationService dataBaseOperationService,
        UserCookieDataModel userCookieDataModel)
    {
        this.eventBus = eventBus;
        AgentContext = signalRAgentHubContext;
        this.dataBaseOperationService = dataBaseOperationService;
        this.llmComEntryPoint = llmComEntryPoint;
        this.databaseRepository = databaseRepository;
        this.userCookieDataModel = userCookieDataModel;

        this.eventBus.Subscribe<CreateNewAgentCommand>(command => { Task.Run(() => CreateNewAgentCommandHandler(command)); });
        this.eventBus.Subscribe<GenerateMessageCommand>(command => { Task.Run(() => GenerateMessageCommandHandler(command)); });
        this.eventBus.Subscribe<LoadAgentByJsonRequestCommand>(command => { Task.Run(() => LoadAgentByJsonRequestHandler(command)); });
        this.eventBus.Subscribe<RemoveAgentCommand>(command => { Task.Run(() => RemoveAgentCommandHandler(command)); });
        this.eventBus.Subscribe<UpdateAgentCommand>(command => { Task.Run(() => UpdateAgentCommandHandler(command)); });
    }

    public async Task CreateNewAgentCommandHandler(CreateNewAgentCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            AgentTemplateDB agentTemplateDB = AgentTemplate.GetInitialAgent().ToDb();
            agentTemplateDB.UserId = cmd.UserId;

            int AgentId = databaseRepository.AddRow(DataBaseTableNames.AgentTemplates.ToString(), agentTemplateDB);
            AgentTemplate agentTemplate = new(AgentId, agentTemplateDB.Name, cmd.UserId, agentTemplateDB.LLMModel,
                agentTemplateDB.Temperature, agentTemplateDB.AllowedTools, "[]");

            await SignalRAgentHub.CreateNewAgentResponse(AgentContext, cmd.ConnectionId, agentTemplate);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to add or update Agent: {ex.Message}");
            throw;
        }
    }

    public async Task GenerateMessageCommandHandler(GenerateMessageCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            LlmStreamSlice? response =  await llmComEntryPoint.RequestChat(cmd.Messages,cmd.Model, cmd.Temperature);

            await SignalRAgentHub.GenerateMessageResponse(AgentContext, cmd.ConnectionId, response?.message ?? "");
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to generate agent message: {ex.Message}");
            throw;
        }
    }

    private async Task LoadAgentByJsonRequestHandler(LoadAgentByJsonRequestCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            AgentTemplate agentTemplate = SerializableObjectBase.FromJson<AgentTemplate>(cmd.AgentJson);

            AgentTemplateDB agentTemplateDB = agentTemplate.ToDb();
            agentTemplateDB.UserId = cmd.UserId;

            int newAgentId = databaseRepository.AddRow(DataBaseTableNames.AgentTemplates.ToString(), agentTemplateDB);
            agentTemplate.Id = newAgentId;

            await SignalRAgentHub.CreateNewAgentResponse(AgentContext, cmd.ConnectionId, agentTemplate);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to add Agent: {ex.Message}");
            throw;
        }
    }

    public async Task RemoveAgentCommandHandler(RemoveAgentCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            AgentTemplateDB agentTemplateDB = new()
            {
                Id = cmd.AgentId,
                UserId = cmd.UserId,
            };

            bool success = databaseRepository.DeleteRow(DataBaseTableNames.AgentTemplates.ToString(), agentTemplateDB);

            if (success)
            {
                await SignalRAgentHub.RemoveAgentResponse(AgentContext, cmd.ConnectionId, cmd.AgentId);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to remove agent: {ex.Message}");
            throw;
        }
    }

    public async Task UpdateAgentCommandHandler(UpdateAgentCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            AgentTemplate agentTemplate = cmd.AgentTemplate;
            AgentTemplateDB agentTemplateDB = agentTemplate.ToDb();

            bool success = databaseRepository.UpdateRowById(DataBaseTableNames.AgentTemplates.ToString(), agentTemplateDB);

            if (success)
            {
                await SignalRAgentHub.UpdateAgentResponse(AgentContext, cmd.ConnectionId, agentTemplate);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to remove agent: {ex.Message}");
            throw;
        }
    }
}
